﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace DomainDriver.Presentation.Silverlight.Base
{
    /// <summary>
    /// The purpose of this class is to perform UI updates on UI thread, but on new call stack.
    /// 
    /// Controls like the DataGrid do not like to be updated on the same call stack as the current edit is happening.
    /// 
    /// Using this class to do the update on a new call stack can fix many update issues.
    /// </summary>
    public class CallStackJumper<ARG1, ARG2, ARG3> : CallStackJumper<ARG1, ARG2>
    {
        public const string Argument3Name = "Argument3";

        public CallStackJumper()
            : base()
        { m_Arguments.Add(Argument3Name, default(ARG3)); }

        public ARG3 Argument3
        {
            get { return (ARG3)m_Arguments[Argument3Name]; }
            set { m_Arguments[Argument3Name] = value; }
        }
    }

    /// <summary>
    /// The purpose of this class is to perform UI updates on UI thread, but on new call stack.
    /// 
    /// Controls like the DataGrid do not like to be updated on the same call stack as the current edit is happening.
    /// 
    /// Using this class to do the update on a new call stack can fix many update issues.
    /// </summary>
    public class CallStackJumper<ARG1, ARG2> : CallStackJumper<ARG1>
    {
        public const string Argument2Name = "Argument2";

        public CallStackJumper()
            : base()
        { m_Arguments.Add(Argument2Name, default(ARG2)); }

        public ARG2 Argument2
        {
            get { return (ARG2)m_Arguments[Argument2Name]; }
            set { m_Arguments[Argument2Name] = value; }
        }
    }

    /// <summary>
    /// The purpose of this class is to perform UI updates on UI thread, but on new call stack.
    /// 
    /// Controls like the DataGrid do not like to be updated on the same call stack as the current edit is happening.
    /// 
    /// Using this class to do the update on a new call stack can fix many update issues.
    /// </summary>
    public class CallStackJumper<ARG1> : CallStackJumper
    {
        public const string Argument1Name = "Argument1";

        public CallStackJumper()
            : base()
        { m_Arguments.Add(Argument1Name, default(ARG1)); }

        public ARG1 Argument1
        {
            get { return (ARG1)m_Arguments[Argument1Name]; }
            set { m_Arguments[Argument1Name] = value; }
        }
    }

    /// <summary>
    /// The purpose of this class is to perform UI updates on UI thread, but on new call stack.
    /// 
    /// Controls like the DataGrid do not like to be updated on the same call stack as the current edit is happening.
    /// 
    /// Using this class to do the update on a new call stack can fix many update issues.
    /// </summary>
    public class CallStackJumper
    {
        private bool m_HasRun;
        private BackgroundWorker m_Worker;
        protected Dictionary<string, object> m_Arguments;

        public event CallStackJumpHandler RunStarted;

        public CallStackJumper()
        {
            m_HasRun = false;
            m_Worker = new BackgroundWorker();
            m_Arguments = new Dictionary<string, object>();

            m_Worker.DoWork += new DoWorkEventHandler(m_Worker_DoWork);
            m_Worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(m_Worker_RunWorkerCompleted);
        }

        public bool HasRun
        {
            get { return m_HasRun; }
        }

        public void Run()
        {
            if (HasRun)
            { throw new InvalidOperationException("The CallStackJumper can only be run once."); }

            m_Worker.RunWorkerAsync();
        }

        private void m_Worker_DoWork(object sender, DoWorkEventArgs e)
        {
            // do nothing
        }

        private void m_Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (RunStarted == null)
            { return; }

            CallStackJumpArgs args = new CallStackJumpArgs(m_Arguments);
            RunStarted(this, args);
        }
    }
}